﻿using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using ZOIL.Framework.Core;
using ZOIL.Framework.Presentation;

namespace ZOIL.Library.Behaviors
{
    [Export(typeof(PluginObjectManipulationBehavior))]
    public class MouseObjectManipulationBehavior : PluginObjectManipulationBehavior
    {
        #region private members

        private static readonly Dictionary<UIElement, ObjectDragInformation> ElementsBeingDragged = new Dictionary<UIElement, ObjectDragInformation>();

        #endregion

        protected override void OnAttached()
        {
            base.OnAttached();

            AssociatedObject.MouseDown += AssociatedObjectMouseDown;
            AssociatedObject.MouseUp += AssociatedObjectMouseUp;
            AssociatedObject.MouseMove += AssociatedObjectMouseMove;
        }

        protected override void OnDetaching()
        {
            AssociatedObject.MouseDown -= AssociatedObjectMouseDown;
            AssociatedObject.MouseUp -= AssociatedObjectMouseUp;
            AssociatedObject.MouseMove -= AssociatedObjectMouseMove;

            base.OnDetaching();
        }

        void AssociatedObjectMouseDown(object sender, MouseButtonEventArgs e)
        {
            if (!ZComponent.IsComponentInteractionEnabled)
                return;

            var element = sender as UIElement;

            if (e.IsTouchOrigin())
                return;

            if (ElementsBeingDragged.ContainsKey(element))
            {
                return;
            }

            var info = new ObjectDragInformation(e.GetPosition(null))
                           {
                               ElementBeingDragged = ZBehavior.FindCanvasChild(element)
                           };

            //set ZIndex of ElementBeingDragged
            if (info.ElementBeingDragged is ZComponent)
                Panel.SetZIndex(info.ElementBeingDragged, ZComponent.ZIndexCounter++);

            info.OriginalFrameworkElement = (e.Source as FrameworkElement != null) ? e.Source as FrameworkElement : info.ElementBeingDragged as FrameworkElement;
            info.Bottom = Canvas.GetBottom(info.ElementBeingDragged);
            info.Top = Canvas.GetTop(info.ElementBeingDragged);
            info.Left = Canvas.GetLeft(info.ElementBeingDragged);
            info.Right = Canvas.GetRight(info.ElementBeingDragged);
            info.OriginalPositionOnUIElement = e.GetPosition(element);

            // TODO: replace using TransformToAncestor()
            info.ParentLandscapes = ZTreeHelper.TryFindParents<ZInformationLandscape>(element);

            ElementsBeingDragged[element] = info;

            element.CaptureMouse();
            e.Handled = true;
        }

        void AssociatedObjectMouseUp(object sender, MouseButtonEventArgs e)
        {
            if (!ZComponent.IsComponentInteractionEnabled)
                return;

            var element = sender as UIElement;

            if (ElementsBeingDragged.ContainsKey(sender as UIElement))
            {
                var info = ElementsBeingDragged[sender as UIElement];

                if (!IsDragging)
                {
                    //apply click zoom here, because we do not drag
                    FrameworkElement zoomTarget = ZTreeHelper.GetClosestZoomTarget(info.ElementBeingDragged as FrameworkElement, info.OriginalFrameworkElement);
                    if (zoomTarget != null)
                    {
                        info.ParentLandscapes.First().ZoomOnto(zoomTarget, false, true);
                        zoomTarget.RaiseEvent(new RoutedEventArgs(ZInformationLandscape.ClickZoomEvent, zoomTarget));
                    }
                }

                ElementsBeingDragged.Remove(sender as UIElement);
            }

            element.ReleaseMouseCapture();
            e.Handled = true;

            //Raise DragFinishedEvent and set IsDragging to false
            FinishDrag(e.GetPosition(null));

            IsDragging = false;
        }

        void AssociatedObjectMouseMove(object sender, MouseEventArgs e)
        {
            if (!ZComponent.IsComponentInteractionEnabled)
                return;

            var element = sender as UIElement;
            if (ElementsBeingDragged.ContainsKey(element))
            {
                var info = ElementsBeingDragged[element];
                var currentPoint = e.GetPosition(null);

                double scale = info.GetScaleFactor();

                var diff = Point.Subtract(currentPoint, info.OriginalPositionOnWindow);

                //double xdiff = (currentPoint.X - info.OriginalMousePositionOnWindow.X);
                //double ydiff = (currentPoint.Y - info.OriginalMousePositionOnWindow.Y);

                if (diff.Length > 5)
                {
                    if (!IsDragging)
                    {
                        IsDragging = true;

                        //Raise OnDragStartedEvent and set IsDragging to true
                        StartDrag(e.GetPosition(null));
                        //element.SetValue(ZObjectDragDropBehavior.IsDraggingProperty, true);
                    }
                    else
                    {
                        //Raise DragDeltaEvent
                        ChangeDrag(currentPoint);
                    }
                }

                double newX = info.Left + diff.X / scale;
                double newY = info.Top + diff.Y / scale;

                Canvas.SetLeft(info.ElementBeingDragged, newX);
                Canvas.SetTop(info.ElementBeingDragged, newY);

                e.Handled = true;
            }
        }
    }
}